//
// Created by tcj on 2022/3/21.
//


#include "address.h"
#include "endian.h"
#include "sstream"
#include "log.h"
#include "netdb.h"
#include "ifaddrs.h"
namespace sylar{
    static sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT();

    template<typename T>
    static T createMask(uint32_t bits){
        return (1<< (sizeof(T)*8-bits))-1;
    }

    template<class T>
    static uint32_t CountBytes(T value){
        uint32_t result=0;
        for(;value;++result){
            value &= value-1;
        }
        return result;
    }

    Adress::ptr
    Adress::LookupAny( const std::string &host,
                      int family, int type, int protocol) {
        std::vector<Adress::ptr> result;
        if(Lookup(result,host,family,type,protocol)){
            return result[0];
        }
        return nullptr;

    }

    IPAdress::ptr
    Adress::LookupAnyIpAddress( const std::string &host, int family, int type,
                               int protocol) {
        std::vector<Adress::ptr> result;
        if(Lookup(result,host,family,type,protocol)){
           for(auto& i:result){
               std::cout<<i->toString()<<std::endl;
               IPAdress::ptr v = std::dynamic_pointer_cast<IPAdress>(i);
               if(v){
                   return v;
               }
           }
        }
        return nullptr;
    }



    bool Adress::Lookup(std::vector<Adress::ptr> &result, const std::string &host,
                        int family, int type, int protocol) {
        addrinfo hints,*results,*next;
        hints.ai_flags=0;
        hints.ai_family=family;
        hints.ai_socktype = type;
        hints.ai_protocol = protocol;
        hints.ai_addrlen=0;
        hints.ai_canonname = NULL;
        hints.ai_addr = NULL;
        hints.ai_next=NULL;

        std::string node;
        const char* service =NULL;

//        检查ipv6 service
        SYLAR_LOG_INFO(g_logger)<<"host="<<host<<std::endl;
        if(!host.empty() && host[0]=='['){
            const char* endipv6 = (const char*)memchr(host.c_str()+1,']', host.size() - 1);
            if(endipv6){
                if(*(endipv6+1) == ':'){
                    service = endipv6+2;
                }
                node = host.substr(1,endipv6-host.c_str()-1);
            }
        }
//        检查node service
        if(node.empty()){
            //memchr(buf,ch,size):在buf的前size中找到ch子符
            service = (const char*) memchr(host.c_str(),':',host.size());
            if(service){
                if(!memchr(service+1,':',host.c_str()+host.size()-service-1)){
                    node = host.substr(0,service-host.c_str());
                    ++service;
                }
            }
        }

        if(node.empty()){
            node = host;
        }
        int error = getaddrinfo(node.c_str(),service,&hints,&results);
        if(error){
            SYLAR_LOG_ERROR(g_logger)<<"Address::Lookup getaddress("<<host<<","
                <<family<<", "<<type<<") err="<<error<<" errstr="<<
                strerror(error)<<std::endl;
            return false;
        }
        next =results;
        while(next){
            result.push_back(Create(next->ai_addr,(socklen_t)next->ai_addrlen));
            next = next->ai_next;
        }
        freeaddrinfo(results);
        return true;
    }

    bool
    Adress::GetInterfaceAddresses(std::multimap<std::string, std::pair<Adress::ptr, uint32_t>> &result,
                                  int family) {
        struct ifaddrs *next,*results;
        if(getifaddrs(&results) !=0){
            SYLAR_LOG_ERROR(g_logger)<<"Address::getinterfaceaddresses getifaddrs "
                <<" err="<<errno<<" errstr="<<strerror(errno);
            return false;
        }
        try{
            for(next = results;next;next = next->ifa_next){
                Adress::ptr  addr;
                uint32_t prefix_len = ~0u;
                if(family !=AF_UNSPEC && family !=next->ifa_addr->sa_family){
                    continue;
                }
                switch(next->ifa_addr->sa_family){
                    case AF_INET:
                    {
                        addr = Create(next->ifa_addr,sizeof(sockaddr_in));
                        uint32_t netmask = ((sockaddr_in*)next->ifa_netmask)->sin_addr.s_addr;
                        prefix_len = CountBytes(netmask);
                    }
                    break;
                    case AF_INET6:
                    {
                        addr = Create(next->ifa_addr,sizeof(sockaddr_in6));
                        in6_addr& netmask = ((sockaddr_in6*)next->ifa_netmask)->sin6_addr;
                        prefix_len =0;
                        for(int i=0;i<16;++i){
                            prefix_len += CountBytes(netmask.s6_addr[i]);
                        }
                    }
                    break;
                    default:
                        break;
                }
                if(addr){
                    result.insert(std::make_pair(next->ifa_name,
                                     std::make_pair(addr,prefix_len)));
                }
            }
        }catch(...){
            SYLAR_LOG_ERROR(g_logger)<<"Address:getinterfaceaddresss exception";
            freeifaddrs(results);
            return false;
        }
        freeifaddrs(results);
        return true;

    }

    bool Adress::GetInterfaceAddresses(std::vector<std::pair<Adress::ptr, uint32_t>> &result,
                                       const std::string &iface,
                                       int family) {
        if(iface.empty() || iface =="*"){
            if(family == AF_INET || family ==AF_UNSPEC){
//                result.push_back(std::make_pair(Adress::ptr(new IPv4Adress()), 0u));
                result.push_back(std::make_pair(Adress::ptr(new IPv4Adress()), 0u));
            }
            if(family == AF_INET6 || family== AF_UNSPEC){
                result.push_back(std::make_pair(Adress::ptr(new IPv6Adress()),0u));
            }
            return true;
        }
        std::multimap<std::string
                ,std::pair<Adress::ptr, uint32_t> > results;

        if(!GetInterfaceAddresses(results,family)) {
            return false;
        }
        auto its = results.equal_range(iface);

        for(;its.first != its.second;++its.first){
            result.push_back(its.first->second);
        }
        return !result.empty();
    }


    int Adress::getFamily() const{
        return getAddr()->sa_family;
    }

    std::string Adress::toString() const{
        std::stringstream ss;
        insert(ss);
        return ss.str();
    }

    Adress::ptr Adress::Create(const sockaddr *addr, socklen_t addrlen) {
        if(addr == nullptr){
            return nullptr;
        }
        Adress::ptr result;
        switch (addr->sa_family) {
            case AF_INET:
                result.reset(new IPv4Adress(*(const sockaddr_in*)addr));
                break;
            case AF_INET6:
                result.reset(new IPv6Adress(*(const sockaddr_in6*)addr));
                break;
            default:
                result.reset(new UnknowAdress(*addr));
                break;
        }
        return result;
    }

    bool Adress::operator<(const Adress &rhs) const {
        socklen_t minlen = std::min(getAddrLen(),rhs.getAddrLen());
//       比较两个字节的前minlen子符
        int result = memcmp(getAddr(),rhs.getAddr(),minlen);
        if(result<0){
            return true;
        }else if(result>0){
            return false;
        }else if(getAddrLen() < rhs.getAddrLen()){
            return true;
        }
        return false;
    }

    bool Adress::operator==(const Adress &rhs) const {
        return getAddrLen() == rhs.getAddrLen()
        && memcmp(getAddr(), rhs.getAddr(), getAddrLen())==0;
//                && memcmp(getAddrLen(),rhs.getAddrLen(),getAddrLen())==0;
    }

    bool Adress::operator!=(const Adress &rhs) const {
        return !(*this==rhs);
    }

    IPAdress::ptr IPAdress::Create(const char *address, uint16_t port) {
        addrinfo hints,*results;
        memset(&hints,0,sizeof (addrinfo));

        hints.ai_flags = AI_NUMERICHOST;
        hints.ai_family = AF_UNSPEC;

        int error = getaddrinfo(address,NULL,&hints,&results);
        if(error){
            SYLAR_LOG_ERROR(g_logger)<<"Ipadress::create("<<address<<
                    ", "<<port<<") error="<<error<<
                    ",errno="<<errno<<" errstr="<<strerror(errno);
            return nullptr;
        }
        try {
            IPAdress::ptr result = std::dynamic_pointer_cast<IPAdress>(
                    Adress::Create(results->ai_addr,(socklen_t)results->ai_addrlen));
            if(result){
                result->setPort(port);
            }
            freeaddrinfo(results);
            return result;
        }catch (...){
            freeaddrinfo(results);
            return nullptr;
        }

    }

    IPv4Adress::ptr IPv4Adress::Create(const char *address, uint16_t port) {
        IPv4Adress::ptr rt(new IPv4Adress);
        rt->m_addr.sin_port = byteswapOnlittleEndian(port);
        int result = inet_pton(AF_INET,address,&rt->m_addr.sin_addr);
        if(result<=0){
            SYLAR_LOG_ERROR(g_logger)<<"ipv4address::create"<<address<<","
                    <<port<<") rt="<<result<<" errno="<<errno
                        <<"errstr="<<strerror(errno);
            return nullptr;
        }
        return rt;
    }

    IPv4Adress::IPv4Adress(const sockaddr_in &address) {
        m_addr = address;
    }

    IPv4Adress::IPv4Adress(uint32_t adress, uint16_t port) {
        memset(&m_addr,0,sizeof(m_addr));
        m_addr.sin_family = AF_INET;
        m_addr.sin_port = byteswapOnlittleEndian(port);
        m_addr.sin_addr.s_addr = byteswapOnlittleEndian(adress);

    }

    const sockaddr *IPv4Adress::getAddr() const {
        return (sockaddr*)&m_addr;
    }

    sockaddr *IPv4Adress::getAddr(){
        return (sockaddr*)&m_addr;
    }

    socklen_t IPv4Adress::getAddrLen() const {
        return sizeof(m_addr);
    }

    std::ostream &IPv4Adress::insert(std::iostream &os) const {
        uint32_t addr = byteswapOnlittleEndian(m_addr.sin_addr.s_addr);
        os<<((addr>>24) & 0xff)<<"."
          <<((addr>>16) & 0xff)<<"."
          <<((addr>>8) & 0xff)<<"."
          <<(addr& 0xff);
        os<<":"<<byteswapOnlittleEndian(m_addr.sin_port);
        return os;
    }

    IPAdress::ptr IPv4Adress::broadcastAdress(uint32_t prefix_len) {
        if(prefix_len>32){
            return nullptr;
        }
        sockaddr_in baddr(m_addr);
        baddr.sin_addr.s_addr |= byteswapOnlittleEndian(
                createMask<uint32_t>(prefix_len));
//        return IPv4Address::ptr(new IPv4Address(baddr));
        return IPv4Adress::ptr(new IPv4Adress(baddr));
    }

    IPAdress::ptr IPv4Adress::networkAdress(uint32_t prefix_len) {
        if(prefix_len>32){
            return nullptr;
        }
        sockaddr_in baddr(m_addr);
        baddr.sin_addr.s_addr &= byteswapOnlittleEndian(
                createMask<uint32_t>(prefix_len));
        return IPv4Adress::ptr(new IPv4Adress(baddr));
    }

    IPAdress::ptr IPv4Adress::subnetMask(uint32_t prefix_len) {
        sockaddr_in subnet;
        memset(&subnet,0,sizeof(subnet));
        subnet.sin_family =AF_INET;
        subnet.sin_addr.s_addr = ~byteswapOnlittleEndian(createMask<uint32_t>(prefix_len));
        return IPv4Adress::ptr(new IPv4Adress(subnet));
    }

    uint32_t IPv4Adress::getPort() const {
        return byteswapOnlittleEndian(m_addr.sin_port);
    }

    void IPv4Adress::setPort(uint16_t v) {
        m_addr.sin_port = byteswapOnlittleEndian(v);
    }

    IPv6Adress::ptr IPv6Adress::Create(const char *address, uint16_t port) {
        IPv6Adress::ptr rt(new IPv6Adress);
        rt->m_addr.sin6_port = byteswapOnlittleEndian(port);
        int result = inet_pton(AF_INET6,address,&rt->m_addr.sin6_addr);
        if(result<=0){
            SYLAR_LOG_ERROR(g_logger)<<"ipv6address::create"<<address<<","
                                     <<port<<") rt="<<result<<" errno="<<errno
                                     <<"errstr="<<strerror(errno);
            return nullptr;
        }
        return rt;
    }

    IPv6Adress::IPv6Adress(const sockaddr_in6 &address) {
        m_addr=address;
    }
    IPv6Adress::IPv6Adress() {
        memset(&m_addr,0,sizeof(m_addr));
        m_addr.sin6_family = AF_INET6;
    }

    IPv6Adress::IPv6Adress(const uint8_t address[16], uint16_t port) {
        memset(&m_addr,0,sizeof(m_addr));
        m_addr.sin6_family = AF_INET6;
        m_addr.sin6_port = byteswapOnlittleEndian(port);
        memcpy(&m_addr.sin6_addr.s6_addr,address,16);
    }

    sockaddr *IPv6Adress::getAddr(){
        return (sockaddr*)&m_addr;
    }

    const sockaddr *IPv6Adress::getAddr() const {
        return (sockaddr*)&m_addr;
    }




    socklen_t IPv6Adress::getAddrLen() const {
        return sizeof(m_addr);
    }

    std::ostream &IPv6Adress::insert(std::iostream &os) const {
        os<<"(";
        uint16_t* addr = (uint16_t*)m_addr.sin6_addr.s6_addr;
        bool used_zeros = false;
        for(size_t i=0;i<8;++i){
            if(addr[i] ==0 && !used_zeros){
                continue;
            }
            if(i && addr[i-1]==0 && !used_zeros){
                os<<":";
                used_zeros = true;
            }
            if(i){
                os<<":";
            }
            os<<std::hex<<(int) byteswapOnlittleEndian(addr[i])<<std::dec;
        }
        if(!used_zeros && addr[7]==0){
            os<<"::";
        }
        os<<"]:"<<byteswapOnlittleEndian(m_addr.sin6_port);
        return os;
    }

    IPAdress::ptr IPv6Adress::broadcastAdress(uint32_t prefix_len) {
        sockaddr_in6 baddr(m_addr);
        baddr.sin6_addr.s6_addr[prefix_len/8] |=
                createMask<uint8_t>(prefix_len);
        for(int i=prefix_len/8+1;i<16;++i){
            baddr.sin6_addr.s6_addr[i] = 0xff;
        }

        return IPv6Adress::ptr(new IPv6Adress(baddr));
    }

    IPAdress::ptr IPv6Adress::networkAdress(uint32_t prefix_len) {
        sockaddr_in6 baddr(m_addr);
        baddr.sin6_addr.s6_addr[prefix_len/8] &=
                createMask<uint8_t>(prefix_len);
        return IPv6Adress::ptr(new IPv6Adress(baddr));
    }

    IPAdress::ptr IPv6Adress::subnetMask(uint32_t prefix_len) {
        sockaddr_in6 subnet;
        memset(&subnet,0,sizeof(subnet));
        subnet.sin6_family = AF_INET6;
        subnet.sin6_addr.s6_addr[prefix_len/8] =
                ~createMask<uint8_t>(prefix_len%8);
        for(uint32_t i=0;i<prefix_len/8;i++){
            subnet.sin6_addr.s6_addr[i] =0xFF;
        }
        return IPv6Adress::ptr(new IPv6Adress(subnet));
    }

    uint32_t IPv6Adress::getPort() const {
        return byteswapOnlittleEndian(m_addr.sin6_port);
    }

    void IPv6Adress::setPort(uint16_t v) {
        m_addr.sin6_port = byteswapOnlittleEndian(v);
    }

//
//    void IPv6Adress::setPort(uint32_t v) {
//        m_addr.sin6_port = byteswapOnlittleEndian(v);
//    }
    static const size_t MAX_PATH_LEN = sizeof(((sockaddr_un*)0)->sun_path)-1;


    UnixAdress::UnixAdress(){
        memset(&m_addr,0,sizeof(m_addr));
        m_addr.sun_family = AF_UNIX;
        m_length = offsetof(sockaddr_un, sun_path) + MAX_PATH_LEN;
    }

    UnixAdress::UnixAdress(const std::string &path) {
        memset(&m_addr,0,sizeof(m_addr));
        m_addr.sun_family = AF_UNIX;
        m_length = path.size()+1;
        if(!path.empty() && path[0] =='\0'){
            --m_length;
        }
        if(m_length>sizeof(m_addr.sun_path)){
            throw  std::logic_error("path too long");
        }

        memcpy(m_addr.sun_path,path.c_str(),m_length);
        m_length += offsetof(sockaddr_un,sun_path);
    }

    sockaddr *UnixAdress::getAddr() {
        return (sockaddr*)&m_addr;
    }

    const sockaddr *UnixAdress::getAddr() const {
        return (sockaddr*) &m_addr;
    }

    socklen_t UnixAdress::getAddrLen() const {
        return m_length;
    }

    void UnixAdress::setAddrLen(uint32_t v) {
        m_length =v;
    }
    std::ostream &UnixAdress::insert(std::iostream &os) const {
        if(m_length> offsetof(sockaddr_un,sun_path) &&
                    m_addr.sun_path[0] =='\0'){
            return os<<"\\0"<<std::string(m_addr.sun_path+1,m_length- offsetof(sockaddr_un,sun_path)-1);
        }
        return os<<m_addr.sun_path;
    }


    UnknowAdress::UnknowAdress(const sockaddr &addr) {
        m_addr =addr;
    }
    UnknowAdress::UnknowAdress(int family) {
        memset(&m_addr,0,sizeof(m_addr));
        m_addr.sa_family = family;
    }

    sockaddr *UnknowAdress::getAddr() {
        return (sockaddr*)&m_addr;
    }

    const sockaddr *UnknowAdress::getAddr() const {
        return &m_addr;
    }

    socklen_t UnknowAdress::getAddrLen() const {
        return sizeof(m_addr);
    }

    std::ostream& UnknowAdress::insert(std::iostream& os) const {
        os<<"[UnknowAdress family="<<m_addr.sa_family<<"]";
        return os;
    }

    std::ostream& operator<<(std::ostream& os, const Adress& addr) {
        return addr.insert(dynamic_cast<std::iostream &>(os));
    }

}
